package com.self.ry.utils.sm2;

import com.self.ry.utils.ByteHexUtil;
import com.self.ry.utils.Util;

import java.io.IOException;
import java.util.Map;

/**
 * 
* Copyright: Copyright (c) 2018
* 
* @ClassName: SM2Util.java
* @Description: 国密SM2算法
*
* @version: v1.0.0
* @author: my
* @date: 2018年10月30日 上午9:27:34
*
* Modification History:
* Date         Author          Version            Description
*---------------------------------------------------------*
* 2018年10月30日     my           v1.0.0               修改原因
 */
public final class SM2Util {
	/**
	 * 国密--商用密码，主要完成 加密、解密、签名、验签、摘要等操作。
	 * 国密SM2是非对称密码算法，是基于ECC算法的非对称算法。 密钥长度为256bit ECB--电码本模型 ECC--椭圆曲线密码算法
	 * 
	 * ECC 算法依赖两个重要的数学运算 1，大数运算，2，椭圆乘法运算。 秘钥生成速度 较RSA算法快百倍以上 解密加密速度 较快。
	 * 公钥 64B 私钥32B 输入数据长度小于2^32 -1 ,输出长度是明文长度+96，有随机数参数，每次密文不同
	 * 
	 * 椭圆曲线密码学（Elliptic curve cryptography)简称ECC
	 * 
	 * 对于服务器来说 公开自己的公钥，让终端使用公钥加密，服务器使用私钥解密
	 * 
	 * 椭圆曲线的方程式：y^2 = x^3 + ax + b
	 * 一个有限域Fp上，选择两个满足下列条件的小于p(p为素数)的非负整数a、b：4a^3 + 27b^2 != 0 (mod p)
	 * 
	 * p,a,b用来确定一条椭圆曲线，G(gx, gy)为基点，n为点G的阶，另外一个非必要参数h，是椭圆曲线上
	 * 所有点的个数m与n相除的整数部分。
	 * SM2公私钥的关系：
	 *    P=d*G 或 (x, y) = d * (gx, gy) ,公钥P是基点G的d倍点。
	 *    P为公钥，坐标(x, y), d为私钥，G为基点坐标(gx, gy)
	 */

	// 私钥
	private static String privateKey;
	// 公钥
	private static String publicKey;

//	// 公钥文件名
//	public static final String FILE_PUBLIC_KEY = Const.PATH + File.separator + "sm2.public";
//	// 私钥文件名
//	public static final String FILE_PRIVATE_KEY = Const.PATH + File.separator+ "sm2.private";

	/*static  {
		synchronized (SM2Util.class) {
			// 查看rsa文件是否存在
			File file = new File(FILE_PUBLIC_KEY);
			if (file.exists()) {
				// 读取公钥
				try (InputStream inputStream = new FileInputStream(FILE_PUBLIC_KEY);
					 ObjectInputStream ois = new ObjectInputStream(inputStream)) {
					publicKey =  ois.readUTF();
					System.out.println("publicKey="+publicKey);
				} catch (Exception e) {
					e.printStackTrace();
				}

				// 读取私钥
				try (InputStream inputStream = new FileInputStream(FILE_PRIVATE_KEY);
					 ObjectInputStream ois = new ObjectInputStream(inputStream)) {
					privateKey = ois.readUTF();
					System.out.println("privateKey="+privateKey);
				} catch (Exception e) {
					e.printStackTrace();
				}
			} else {
				Map<String, String> keyPair = SM2Factory.createKeyPair();
				publicKey = keyPair.get(Const.PUBLIC_KEY);
				System.out.println("publicKey="+publicKey);
				privateKey = keyPair.get(Const.PRIVATE_KEY);
				System.out.println("privateKey="+privateKey);
				// 写入文件
				try (ObjectOutputStream oos1 = new ObjectOutputStream(new FileOutputStream(FILE_PUBLIC_KEY))) {
					oos1.writeUTF(publicKey);
				} catch (Exception e) {
					e.printStackTrace();
				}

				try (ObjectOutputStream oos1 = new ObjectOutputStream(new FileOutputStream(FILE_PRIVATE_KEY))) {
					oos1.writeUTF(privateKey);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		};
	}*/

	/**
	 * 公钥加密
	 * @param data 要加密的明文 --- 十六进制字符串
	 * @return 加密后的密文---十六进制字符串
	 */
	public static String encode(String publicKey, byte[] data){
		byte[] source = data;
		return encode(Util.hexToByte(publicKey), source);
	}

	/**
	 * 公钥加密
	 * @param source 字节数组 明文
	 * @return 加密后的密文---十六进制字符串
	 */
	public static String encode(byte[] publicKeyBytes, byte[] source){
		return SM2EnDecryption.encrypt(publicKeyBytes, source);
	}

	/**
	 * 私钥解密
	 * @param privateKey 密钥
	 * @param source 密文 -- 十六进制的字符串
	 * @return 解密后的字节数组  明文
	 * @throws IOException
	 */
	public static byte[] decode(String privateKey, String source) throws IOException {
		return decode(Util.hexToByte(privateKey), Util.hexToByte(source));
	}

	/**
	 * 私钥解密
	 * @param source  字节数组 密文
	 * @return 解密后的字节数组  明文
	 * @throws IOException
	 */
	public static byte[] decode(byte[] privateKeyBytes,  byte[] source) throws IOException {
		return SM2EnDecryption.decrypt(privateKeyBytes, source);
	}


	public static void main(String[] args) throws Exception {
		// 生成密钥对
		//Map<String, String> keyPair = SM2Factory.createKeyPair();

		String text = "测试";
		byte[] source = text.getBytes();
		//String publicKey ="FA05C51AD1162133DFDF862ECA5E4A481B52FB37FF83E53D45FD18BBD6F32668A92C4692EEB305684E3B9D4ACE767F91D5D108234A9F07936020A92210BA9447";
		String publicKey ="46A6BE18873B57E759CE6864F08EF19AE555FF3D107E3181532C83ED1622B8C54100026F4D4FACA79E389FA0F7ABAB271F756C2B638BDA435B6DD4F6E695EEDC";

		String result = encode(publicKey, source);
		//String result = SM2EnDecryption.encrypt(Util.hexToByte(publicKey), source);
		//System.out.println(Base64.encode(result));
		System.out.println("加密前长度="+source.length);
		System.out.println("加密后长度="+result.getBytes().length);
		//String privatekey = "5EB4DF17021CC719B678D970C620690A11B29C8357D71FA4FF9BF7FB6D89767A";
		String privatekey = "372769C09F326EE7B00B0F309236AB6803E3D883FFC7068033E5ED156A0EAAFB";
		//byte[] decryptresult = SM2EnDecryption.decrypt(Util.hexToByte(privatekey), Util.hexToByte(result));
		byte[] decryptresult = decode(privatekey, result);
		System.out.println(new String(decryptresult));
	}
}


